package com.lagou.cl.factory;

import com.lagou.cl.annotation.*;
import com.lagou.cl.utils.FirstCharUtils;
import com.lagou.cl.utils.JavaPackageFileUtil;
import org.apache.commons.lang3.StringUtils;

import java.io.FileNotFoundException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

/**
 * @author 程  林
 * @date 2020-04-01 10:22
 * @description // 工厂类，生产对象（使用反射技术）
 * @since V1.0.0
 */
public class BeanFactory {

	private static Logger logger = Logger.getLogger(BeanFactory.class.getName());

	/**
	 * 存储对象（只有自定义注解的类才被实例化）
	 */
	private static Map<String, Object> BeanMap = new HashMap<>();

	/**
	 * 以接口的类名的容器
	 */
	private static Map<String, Object> InterfaceBeanMap = new HashMap<>();

	private static ProxyFactory proxyFactory;

	private static List<Class> myDefinition = new ArrayList<>();


	static {

		myDefinition.add(MyService.class);
		myDefinition.add(MyComponet.class);
		myDefinition.add(MyRepository.class);
		myDefinition.add(MyTransactions.class);

		String basePackageName = "com.lagou.cl";
		logger.info("加载 " + basePackageName + " 路径下的所有类");

		List<Class<?>> classFromPackage = JavaPackageFileUtil.getClassFromPackage(basePackageName);

		if (classFromPackage.isEmpty()) {
			try {
				throw new FileNotFoundException(basePackageName + " 下的文件不存在");
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}
		//1 进行实例化
		classFromPackage.forEach(BeanFactory::doCreateBean);

		//2 依赖注入
		populateBean();

		//3 创建事务的代理对象
		createTransactionProxyObj();

		logger.info("加载 " + basePackageName + " 路径下的所有类 - 完成");
		BeanMap.forEach((beanId, bean) -> {
			logger.info("已加载到容器中的bean： " + beanId + " = " + bean.toString());
		});
	}

	private static void createTransactionProxyObj() {

		if (null == proxyFactory) {
			proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
		}

		//找到我们自定义的MyTransactions所在的方法，生成代理对象，覆盖原来的对象
		BeanMap.forEach(BeanFactory::createTransactionProxyObj);
	}

	private static void createTransactionProxyObj(String beanId, Object beanInstance) {

		Method[] methods = beanInstance.getClass().getMethods();
		for (Method method : methods) {
			//当前方法上有自定义的事务注解
			if (method.isAnnotationPresent(MyTransactions.class)) {
				MyTransactions myTransactions = method.getAnnotation(MyTransactions.class);
				//设置事务的属性（隔离级别、传播行为等）。。。

				Class<?>[] interfaces = beanInstance.getClass().getInterfaces();
				//service层是否实现接口的情况【jdk还是cglib】
				Object proxyObj = null;
				if (null != interfaces && interfaces.length > 0) {
					logger.info("使用JDK动态代理");
					proxyObj = proxyFactory.getJdkProxy(beanInstance);
				} else {
					logger.info("使用CGLIB动态代理");
					proxyObj = proxyFactory.getCglibProxy(beanInstance);
				}

				BeanMap.put(beanId, proxyObj);
			}
		}

	}

	private static void populateBean() {

		if (BeanMap.isEmpty()) {
			logger.info("容器中没有实例化的Bean");
			return;
		}


		BeanMap.forEach((beanId, beanInstance) -> {

			//获取实例化的对象的属性
			Field[] declaredFields = beanInstance.getClass().getDeclaredFields();
			for (Field field : declaredFields) {
				Boolean hasAutowired = false;
				Boolean hasquerlifer = false;

				Annotation[] annotations = field.getAnnotations();
				for (Annotation annotation : annotations) {
					if (annotation.annotationType().equals(MyAutowired.class)) {
						hasAutowired = true;
						if (annotation.annotationType().equals(MyQuerlifer.class)) {
							hasquerlifer = true;
						}
						break;
					}
				}

				//需要被注入
				if (hasAutowired) {
					field.setAccessible(true);
					//

					String propertyBeanId = "";
					if (hasquerlifer) {
						//定义了别名
						propertyBeanId = field.getAnnotation(MyQuerlifer.class).value();
					} else {
						propertyBeanId = FirstCharUtils.toLowerCaseFirstOne(field.getType().getSimpleName());
					}

					//从容器中获取该Bean
					Object propertyBean = getBean(propertyBeanId);
					if (null == propertyBean) {
						// 以接口注入
						propertyBeanId = FirstCharUtils.toLowerCaseFirstOne(field.getType().getSimpleName());
						propertyBean = InterfaceBeanMap.get(propertyBeanId);
					}

					try {
						//通过反射注入属性的值
						field.set(beanInstance, propertyBean);
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
				}
			}
		});
	}

	/**
	 * 使用反射创建类的实例
	 *
	 * @param clazz 需要被实例化的类
	 */
	private static void doCreateBean(Class<?> clazz) {


		//1接口和自定义注解不需要实例化

		if (clazz.isAnnotation() || clazz.isInterface()) {
			return;
		}

		//只需要实例化自定义的注解(MyRepository/MyService/MyTransactions)的类

		Annotation[] annotations = clazz.getAnnotations();
		for (Annotation annotation : annotations) {
			Class<? extends Annotation> annotationType = annotation.annotationType();
			if (myDefinition.contains(annotationType)) {
				hasBeanInstance(clazz, annotation, annotationType);
				break;
			}
		}
	}


	private static void hasBeanInstance(Class<?> clazz, Annotation annotation, Class<?> annoClass) {

		String beanId = "";

		if (annoClass.equals(MyService.class)) {
			MyService myService = (MyService) annotation;
			beanId = myService.value();
		}
		if (annoClass.equals(MyRepository.class)) {
			MyRepository myRepository = (MyRepository) annotation;
			beanId = myRepository.value();
		}
		if (annoClass.equals(MyTransactions.class)) {
			MyTransactions myTransactions = (MyTransactions) annotation;
			beanId = myTransactions.value();
		}
		if (annoClass.equals(MyComponet.class)) {
			MyComponet myComponet = (MyComponet) annotation;
			beanId = myComponet.value();
		}

		//判断是否设置的Bean的id
		if (StringUtils.isBlank(beanId)) {
			beanId = FirstCharUtils.toLowerCaseFirstOne(clazz.getSimpleName());
		}
		//1.先判断是否在Map中已存在
		Object o = BeanMap.get(beanId);
		if (null != o) {
			logger.info(beanId + " 在容器中已存在");
			return;
		}

		//创建
		Object beanInstance = null;
		try {
			beanInstance = clazz.newInstance();
		} catch (InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		}

		//放入Map
		BeanMap.put(beanId, beanInstance);
		for (Class<?> clazzInterface : clazz.getInterfaces()) {
			//接口（因为注入式我们是通过接口的注入，多态），有多个接口？
			InterfaceBeanMap.put(FirstCharUtils.toLowerCaseFirstOne(clazzInterface.getSimpleName()), beanInstance);
		}

	}


	/**
	 * 对外提供获取实例对象的接口（根据id获取）
	 *
	 * @param beanId BeanId
	 * @return Bean对象
	 */
	public static Object getBean(String beanId) {
		return BeanMap.get(beanId);
	}

//	public static void main(String[] args) {
//		new BeanFactory();
//		BeanMap.forEach((k, v) -> System.out.println(k + " = " + v));
//	}

}
